package br.unifor.lec.aco;

import java.util.ArrayList;
import java.util.List;

import br.unifor.lec.aco.graph.Grafo;

public class ACO {

	private double[][] pheromone;

	private Grafo graph;

	private int numAnt;

	private int[] currentVertice;

	private int[] tempoRetorno;
	
	@SuppressWarnings("rawtypes")
	private ArrayList[] alvosjaEscolhidos;
	
	private Parameters p;

	private Statistic statistic = new Statistic();

	private final int[] antInicialVertice;

	private int interations = 0;

	public ACO(Parameters p, Grafo graph, int[] antInicialVertice) {
		this.p = p;
		this.graph = graph;
		int numVertices = graph.getNumVertices();
		pheromone = new double[numVertices][numVertices];
		this.numAnt = antInicialVertice.length;
		this.currentVertice = new int[numAnt];
		this.tempoRetorno = new int[numAnt];
		this.alvosjaEscolhidos= new ArrayList[numAnt];
		this.antInicialVertice = antInicialVertice;
					
		for (int i = 0; i < numAnt; i++) {
			currentVertice[i] = antInicialVertice[i];
			tempoRetorno[i]=this.p.tempoRetornoGateway;
			alvosjaEscolhidos[i]= new ArrayList<Integer>();
		}
	}
	
	@SuppressWarnings("unchecked")
	public void run() {
		while (!stop()) {
			//evaporando feromonio
			for (int i = 0; i < this.graph.getNumVertices(); i++) {
				for (int j = 0; j < this.graph.getNumVertices(); j++) {
					pheromone[i][j]= pheromone[i][j]* p.ro;
				}
			}
			
			// para cada formiga
			for (int i = 0; i < numAnt; i++) {
				// para cada vertice vizinho a posicao corrente da formiga
				List<Integer> adjacents = graph.getVertices()[currentVertice[i]].adjacents;
				double[] prob = new double[adjacents.size()];
				double total = 0;

				for (int j = 0; j < adjacents.size(); j++) {
					prob[j] = acoValue(pheromone[i][adjacents.get(j)],
							graph.getVertices()[adjacents.get(j)].d);
					total += prob[j];
				}

				double r = Math.random() * total;
				int k;
				for (k = 0; r > 0; k++) {
					r -= prob[k];
				}

				int chosenVertice = adjacents.get(k - 1);

				pheromone[i][chosenVertice] = pheromone[chosenVertice][i] += p.pheronomeInc;
				if (graph.getVertices()[chosenVertice].target && !alvosjaEscolhidos[i].contains(chosenVertice)) {
					statistic.register(chosenVertice);
					if(tempoRetorno[i]==1) {
						currentVertice[i] = antInicialVertice[i];
						tempoRetorno[i]=p.tempoRetornoGateway;
						alvosjaEscolhidos[i].clear();
					}else {
						tempoRetorno[i]--;
						alvosjaEscolhidos[i].add(chosenVertice);
					}
				} else {
					currentVertice[i] = chosenVertice;
				}
			}
		}
	}

	private boolean stop() {
		return (++interations == p.qtdTics);
	}

	private double acoValue(double pheromone, double d) {
		return (Math.pow(pheromone, p.alpha) == 0 ? 1 : Math.pow(pheromone,
				p.alpha)) * Math.pow(d, p.beta);
	}

	public Statistic getStatistic() {
		return statistic;
	}

	public Grafo getGraph() {
		return graph;
	}

	public void setGraph(Grafo graph) {
		this.graph = graph;
	}

}
